require 'pp'
require 'depac-common/softlayer'
require 'depac-deployment/environment_configuration'
require 'depac-common/constants'
require 'securerandom'
require 'fileutils'
require 'yaml'

# This method builds the Felx Image name for Database using the build label provided and optionally checks whether this image exists in the SoftLayer Flex Image Library
def getDatabaseFlexImageNamebyBuildLabel(buildLabel, validation)
	imageName = "AFC Database Snapshot #{buildLabel}"
	
	if(validation)
		if(!validateFlexImage(imageName))
			Depac::Log.write("INFO", "The Database snapshot '#{imageName}' that corresponds to the build with buildlabel '#{buildLabel}' was not found in the SoftLayer Flex Image Library")
			exit
		end
	end
		return imageName	
end

# This method builds the Felx Image name for Foundation using the build label provided and optionally checks whether this image exists in the SoftLayer Flex Image Library
def getFoundationFlexImageNamebyBuildLabel(buildLabel, validation)
	imageName = "AFC Foundation Snapshot #{buildLabel}"
	
	if(validation)
		if(!validateFlexImage(imageName))
			Depac::Log.write("INFO", "The Foundation snapshot '#{imageName}' that corresponds to the build with buildlabel '#{buildLabel}' was not found in the SoftLayer Flex Image Library")
			exit
		end
	end
		return imageName	
end

# This method builds the Felx Image name for AppServer using the build label provided and optionally checks whether this image exists in the SoftLayer Flex Image Library
def getAppServerFlexImageNamebyBuildLabel(buildLabel, validation)
	imageName = "AFC Appserver Snapshot #{buildLabel}"
	
	if(validation)
		if(!validateFlexImage(imageName))
			Depac::Log.write("INFO", "The AppServer snapshot '#{imageName}' that corresponds to the build with buildlabel '#{buildLabel}' was not found in the SoftLayer Flex Image Library")
			exit
		end
	end
		return imageName	
end

# This method checks whether the Flex image exists in the SoftLayer Flex Image Library
def validateFlexImage(imageName)
	imageDetails = $slmgr.findFlexImageByName(imageName)
	
	if(imageDetails == nil)
		return false
	else
		return true
	end
end

# This method checks whether the vlan exists 
def validateVlanId(vlanId)
  vlanDetails = $slmgr.findVlanDetailsById(vlanId)
  
  if(vlanDetails == nil)
    return false
  else
    return true
  end
end

def getHostnames(build_label, env, type, role, no_of_nodes, existing_env_node_count)
	hostnames = Array.new
	
	no_of_nodes_i = no_of_nodes.to_i
	existing_env_node_count_i = existing_env_node_count.to_i
	
	for i in 1..no_of_nodes_i
	   hostnameString = "#{build_label}-#{env}-#{type}-#{role}-#{i+existing_env_node_count_i}".downcase
	   #hostnames.push("#{hostnameString[0..2]}#{hostnameString[22..hostnameString.length-1]}")
	   hostnames.push(hostnameString)
	end
	return hostnames
end

def getSvrs(svr_nodes)
  unless svr_nodes then return nil; end
	svrs = Array.new
  svr_nodes.each do |svr_name_map|
    svrs.push(svr_name_map['server_name'])
	end
	return svrs
end


# Update the output structure with the node info such hostname and ip address.
def updateOutputWithNodeInfo(dbConfig, hostnameIPArr)
	dbConfig[Depac::CloudDeploymentConstants::NODE_INFO] = hostnameIPArr
end

# Generate the output YAML file.
def generateOutputYAML(outputConfig, fileName, path)
	if(path == nil)
		filePath = "#{File.expand_path("~/DeploymentOutput")}"
	else
		filePath = "#{File.expand_path(path)}"
	end
	
	FileUtils.mkdir_p "#{filePath}"
	
	File.open("#{filePath}/#{fileName}.yml","w") {|f|
		f.puts(outputConfig.to_yaml())
		f.close
	}
end

# Update the output structure with the environment name.
def updateOutputWithEnvironmentName(envConfig, envName)
	envConfig['environment'] = envName
end

# Update the output structure with the build label.
def updateOutputWithBuildLabel(envConfig, buildLabel)
	envConfig['build_label'] = buildLabel
end

# Changing the file permission of private key file to '400' to comply with ssh requirement
def changePrivateKeyFilePermission(filePath)
	File.chmod(0400, File.expand_path(filePath))
end

# Validate the arguments passed.
def validateArgumentsByCommand(option_parser_instance)
	optionalParameter = option_parser_instance.getOptions()
	mandatoryParameter = option_parser_instance.getMandatoryArgs()
	commandName = option_parser_instance.getCommandName()
	
	case commandName 
		when 'createEnvironment'
			buildLabel = mandatoryParameter[0]
			configFile = mandatoryParameter[1]
			
			# Checking whether config file existing or not
			if (!File.exists?(configFile))
				Depac::Log.write("ERROR", "The config file '#{configFile}' doesn't exist.")
				usage(2)
			end
					
		when 'deleteEnvironment'
			#TO DO
			#validate the config file exist or not
	end	
end

def parseArgumentsByCommandName(commandName)
	option_parser_instance = OptionParserClass.new(commandName)
	option_parser_instance.parse()
	
	return option_parser_instance
end


# Exit with an appropriate error message based on the exit code.
def usage(exitcode)
#	if(exitcode == 1)
#		puts "Usage: createEnvironment <build-label> <config-file-path>"
#	else(exitcode == 3)
#		puts "Usage: deleteEnvironment <config-file-path>"
#	end
	exit exitcode
end

def computeDuration(diffTime)
    secs  = diffTime.to_i
    mins  = secs / 60
    hours = mins / 60
    days  = hours / 24

    duration = ""
    
    if days > 0
      duration = "#{days} days and #{hours % 24} hours"
    elsif hours > 0
      duration = "#{hours} hours and #{mins % 60} minutes"
    elsif mins > 0
      duration = "#{mins} minutes and #{secs % 60} seconds"
    elsif secs >= 0
      duration = "#{secs} seconds"
    end
    return duration
end

# Update the output with the node info.
def updateOutputWithNodeInformation(configObject, nodeInfoMap)

	typeArr = getEnvironmentTypes()
	
	typeArr.each do |type|
		if(nodeInfoMap[type] != nil)
			nodeInfoMap[type].each do |role, nodeInfoArr|
				roleConfig = configObject.getNodeConfigurationByRoleAndType(role, type)
				roleConfig[Depac::CloudDeploymentConstants::NODE_INFO] = nodeInfoArr
			end
		end
	end
end

# Returns the environment type in an Array
def getEnvironmentTypes()
	typeArr = Array.new
	typeArr[0] = Depac::CloudDeploymentConstants::AUTHORING_NODES
	typeArr[1] = Depac::CloudDeploymentConstants::RUNTIME_NODES
	typeArr[2] = Depac::CloudDeploymentConstants::SHARED_NODES
	
	return typeArr
end

def updateOutputYAML(outputConfig, fileNamePath)
	fileName = File.basename(fileNamePath, ".yml") 
	path = File.dirname(fileNamePath)
	
	generateOutputYAML(outputConfig, fileName, path)
end

def deleteEnvironment(type, configObject, flag)

	roles =  configObject.getRoleArrayByType(type)
	if roles.length > 0
	
		#By default following role wont be deleted. Removing those role from roles array
		roles.delete(Depac::CloudDeploymentConstants::ROLE_DATABASE)
		roles.delete(Depac::CloudDeploymentConstants::ROLE_SEARCH)
		roles.delete(Depac::CloudDeploymentConstants::ROLE_JMS)
		
		#add the role that user dont want to delete by indicating command line flag value
		roles = roles + flag
		roles.each do |role|
			hostIPArray = configObject.getHostnameIPMapByRoleAndType(role, type)
			hostIPArray.each do |hostIPMap|
				hostIPMap.each do |machine|
					hostName = machine['hostname']
					ip = machine['IP']
					vmid = machine['vmid']
					if hostName != nil
						Depac::Log.write("INFO", "Deleting Machine: hostname=#{hostName} ip=#{ip} vmid=#{vmid}")
						#deleteInstanceByIP(ip)
						$slmgr.deleteInstanceByVMID(vmid)
					end
				end
			end
		end
	end
end

# build the flex image name by role, build label and type based on the naming conventions read from the config file - imageNames.yml
def buildFlexImageNameByRoleBuildLabelAndType(role, buildLabel, type, validate)
	if($imageNameYml == nil)
		imageName_yml_file = Depac::CloudDeploymentConstants::IMAGENAME_FILE_PATH
		imageName_yml_path = Gem::Specification.find_by_name(Depac::CloudDeploymentConstants::CLOUD_DEPL_GEM_NAME).gem_dir + imageName_yml_file
		$imageNameYml = YAML.load_file(imageName_yml_path)
	end
	
	if($imageNameYml[Depac::CloudDeploymentConstants::NAMING_CONVENTION][Depac::CloudDeploymentConstants::ROLES] != nil && $imageNameYml[Depac::CloudDeploymentConstants::NAMING_CONVENTION][Depac::CloudDeploymentConstants::ROLES][role] != nil)
		templateImageName = $imageNameYml[Depac::CloudDeploymentConstants::NAMING_CONVENTION][Depac::CloudDeploymentConstants::ROLES][role]
	else
		templateImageName = $imageNameYml[Depac::CloudDeploymentConstants::NAMING_CONVENTION][Depac::CloudDeploymentConstants::DEFAULT_KEY]
	end
	
	# Build the template filler map with all the possible arguments
	templateFillerMap = Hash.new
	templateFillerMap[:role] = role.capitalize
	templateFillerMap[:type] = type
	templateFillerMap[:buildLabel] = buildLabel
	
	# Format the template image name by replacing the markers with arguments
	imageName = templateImageName % templateFillerMap
	
	# Validate the existence of the image, based on the flag provided
	if(validate)
	  Depac::Log.write("INFO", "-> #{imageName}")
		if(!validateFlexImage(imageName))
			Depac::Log.write("INFO", "The #{role} snapshot '#{imageName}' that corresponds to the build with buildlabel '#{buildLabel}' was not found in the SoftLayer Flex Image Library")
			exit
		end
	end
	return imageName
end

def buildAndValidateImageNames(envConfigObj, buildLabel)
	Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
	Depac::Log.write("INFO", Depac::CloudDeploymentConstants::VALIDATING_IMAGE_TEMPLATES)
	
	typeArr = getEnvironmentTypes()
	envConfig = envConfigObj.getEnvironmentConfiguration()
	
	typeArr.each do |type|
		if(envConfig[type] != nil)
			envConfig[type].each do |config|
				role = config[Depac::CloudDeploymentConstants::ROLE]
				snapshotName = buildFlexImageNameByRoleBuildLabelAndType(role, buildLabel, type, true)
				config[Depac::CloudDeploymentConstants::SNAPSHOT] = snapshotName
			end
		end
	end
end

#Docker
#Need validata Runtime Host/App Host/DB/JMS image on SL
#Also need to valide Runtime and App docker image on Docker register
def buildAndValidateImageNamesForDocker(envConfigObj, buildLabel)
    Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)
	Depac::Log.write("INFO", Depac::CloudDeploymentConstants::VALIDATING_IMAGE_TEMPLATES)
	
	typeArr = getEnvironmentTypes()
	envConfig = envConfigObj.getEnvironmentConfiguration()
	
	typeArr.each do |type|
		if(envConfig[type] != nil)
			envConfig[type].each do |config|
				role = config[Depac::CloudDeploymentConstants::ROLE]
				
				Depac::Log.write("INFO", "current role is #{role}")
				
				if role == Depac::CloudDeploymentConstants::ROLE_DATABASE || role == Depac::CloudDeploymentConstants::ROLE_JMS
				   snapshotName = buildFlexImageNameByRoleBuildLabelAndType(role, buildLabel, type, true)
				   config[Depac::CloudDeploymentConstants::SNAPSHOT] = snapshotName
				else
				   if role == Depac::CloudDeploymentConstants::ROLE_APPSERVER
				     #validate Runtime and App Host image on SL
				     snapshotName = "Docker-AppHost"
				     config[Depac::CloudDeploymentConstants::SNAPSHOT] = snapshotName
				     if(!validateFlexImage(snapshotName))
				       Depac::Log.write("INFO", "The #{role} snapshot '#{snapshotName}' that corresponds to the build with buildlabel '#{buildLabel}' was not found in the SoftLayer Flex Image Library")
			           exit
				     end
				     Depac::Log.write("INFO", "-> #{snapshotName}")
				   else
				      #validate Runtime and Agent Host image on SL
				      snapshotName = "Docker-AgentHost"
				      config[Depac::CloudDeploymentConstants::SNAPSHOT] = snapshotName			      
				      if(!validateFlexImage(snapshotName))
				        Depac::Log.write("INFO", "The #{role} snapshot '#{snapshotName}' that corresponds to the build with buildlabel '#{buildLabel}' was not found in the SoftLayer Flex Image Library")
			            exit
				      end
				      Depac::Log.write("INFO", "-> #{snapshotName}")
				   end  
				end 
			end
		end
	end
	
	# we also need to validate docker image on docker register by search them
	# this validate logic will be a backlog
end

#docker poc
def buildNodeInfoParamMap(existingEnvConfigObject)
  nodeInfoParamMap = Hash.new
  
  envConfig = existingEnvConfigObject.getEnvironmentConfiguration()
  typeArr = getEnvironmentTypes()
  
  typeArr.each do |type|
    if(type != Depac::CloudDeploymentConstants::SHARED_NODES && envConfig[type] != nil)
      roleInfoMap = Hash.new
      envConfig[type].each do |nodeConfig|
      
        if(nodeConfig[Depac::CloudDeploymentConstants::ROLE] == Depac::CloudDeploymentConstants::ROLE_DATABASE)
          roleInfoMap[Depac::CloudDeploymentConstants::ROLE_DATABASE] = nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]
        end
        
        if(nodeConfig[Depac::CloudDeploymentConstants::ROLE] == Depac::CloudDeploymentConstants::ROLE_JMS)
          roleInfoMap[Depac::CloudDeploymentConstants::ROLE_JMS] = nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]
        end
        if(nodeConfig[Depac::CloudDeploymentConstants::ROLE] == Depac::CloudDeploymentConstants::ROLE_AGENT)
          roleInfoMap[Depac::CloudDeploymentConstants::ROLE_AGENT] = nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]
        end
	 
        if(nodeConfig[Depac::CloudDeploymentConstants::ROLE] == Depac::CloudDeploymentConstants::ROLE_INTG)
          roleInfoMap[Depac::CloudDeploymentConstants::ROLE_INTG] = nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]
        end

	 if(nodeConfig[Depac::CloudDeploymentConstants::ROLE] == Depac::CloudDeploymentConstants::ROLE_APPSERVER)
          roleInfoMap[Depac::CloudDeploymentConstants::ROLE_APPSERVER] = nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]
        end
      end
      nodeInfoParamMap[type] = roleInfoMap
    end
  end
  return nodeInfoParamMap
end

#Added by CDL to validate if the configured ihs server has been registed on chef server 20141223
def validateRegistedCA(ihsserverIP)
  caDBKey = nil
  if ihsserverIP
     if (File.exists?(Depac::CloudDeploymentConstants::CADB_PWD_REG_FILE_PATH) && (caDBKeyList = YAML.load_file(Depac::CloudDeploymentConstants::CADB_PWD_REG_FILE_PATH)) != nil) 
         #Depac::Log.write("INFO", "CADB PWD List is #{caDBKeyList }")
         if caDBKeyList[ihsserverIP]["pwd"] && File.exists?("ihs_keystore/#{ihsserverIP}/ihsserverkdb.jks")
            caDBKey= caDBKeyList[ihsserverIP]["pwd"]  
         else
            Depac::Log.write("INFO", " can not find ihs_keystore/#{ihsserverIP}/ihsserverkdb.jks or ihsserver ssl pwd is null")        
         end
     else
         Depac::Log.write("INFO", "Can not find : #{Depac::CloudDeploymentConstants::CADB_PWD_REG_FILE_PATH}")
     end
 end 
 return caDBKey
end 

#Added by CDL to handle any post action when all previous steps complete
def postHandler(envConfig)    
    
    if envConfig !=nil   
      if envConfig["postHandlerEnable"] != false
          
          Depac::Log.write("INFO", "Start post handler process......")
          
          postActionArr= envConfig["postHandlerActions"]
          
          if postActionArr !=nil
    
                postActionArr.each do |postAction|
             
                 action = postAction["action"]
                 
                 case action          
                 when "syncNodeInfoToMonitor"
                      post_SyncNodeToMonitor(envConfig, postAction)
                 else
                   Depac::Log.write("INFO", "Unknow action : #{action} will not be process......")
                 end
                 
             end
          else     
             Depac::Log.write("INFO", "Can not find any post action definition......")   
          end
           
          Depac::Log.write("INFO", "Finish post handler process......")
           
      end
    end
end

def post_SyncNodeToMonitor(envConfig, postAction)
     if postAction["monitorServer"] !=nil && postAction["enable"] = true            
        nodeInfoMap=Hash.new
         
        if envConfig[Depac::CloudDeploymentConstants::RUNTIME_NODES] !=nil
       
            envConfig[Depac::CloudDeploymentConstants::RUNTIME_NODES].each do |nodeConfig|
              role=nodeConfig[Depac::CloudDeploymentConstants::ROLE]
              
              if nodeConfig["node_info"] !=nil
                 nodeInfoArr=nodeConfig["node_info"]                
                 nodeInfoArr.each do |node|
                     if node["IP"] !=nil
                        nodeInfoMap[node["IP"]]=role
                        Depac::Log.write("INFO", "Method post_SyncNodeToMonitor: sync node #{node["IP"]}/#{role}......")
                     end
                 end              
             end
            end
               
            if nodeInfoMap.length !=0
               generateOutputYAML(nodeInfoMap, "post_syncnodeList", nil)    
               Depac::Log.write("INFO", "sync nodes info to monitor server #{envConfig["monitorServer"]}.....")
               cmdstr = "scp -o StrictHostKeyChecking=no -i #{Depac::CloudDeploymentConstants::CLD_DPL_KEYPATH} ~/DeploymentOutput/post_syncnodeList.yml root@#{postAction["monitorServer"]}:#{postAction["destinationPath"]}"
               Depac::Log.write("INFO", "Executing command: " + cmdstr)
               output =  `#{cmdstr}`
               Depac::Log.write("INFO", output)
               Depac::Log.write("INFO", Depac::CloudDeploymentConstants::BORDER_LINE)           
           end                   
       end
     end 
end

#validateArguments(ARGV)

#pp getHostnames("20130722-1830", "FVT", "appserver", 5, 5)
	
#pp getDatabaseFlexImageNamebyBuildLabel("20130723-1830", true);

#pp getAppServerFlexImageNamebyBuildLabel("20130723-1830", false);
#docker poc
def buildNodeInfoMap(existingEnvConfigObject,currentRole)
  nodeInfoParamMap = Hash.new
  
  envConfig = existingEnvConfigObject.getEnvironmentConfiguration()
  typeArr = getEnvironmentTypes()
  
  typeArr.each do |type|
    if(type != Depac::CloudDeploymentConstants::SHARED_NODES && envConfig[type] != nil)
      roleInfoMap = Hash.new
      envConfig[type].each do |nodeConfig|
      
        if(nodeConfig[Depac::CloudDeploymentConstants::ROLE] == currentRole)
          roleInfoMap[currentRole] = nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]
        end
      end
      nodeInfoParamMap[type] = roleInfoMap
    end
  end
  return nodeInfoParamMap
end
#docker poc
def buildNodeInfoMapWithCount(existingEnvConfigObject,currentRole,count)
  nodeInfoParamMap = Hash.new
  
  envConfig = existingEnvConfigObject.getEnvironmentConfiguration()
  typeArr = getEnvironmentTypes()
  
  typeArr.each do |type|
    if(type != Depac::CloudDeploymentConstants::SHARED_NODES && envConfig[type] != nil)
      roleInfoMap = Hash.new
      envConfig[type].each do |nodeConfig|
      
        if(nodeConfig[Depac::CloudDeploymentConstants::ROLE] == currentRole)
	   roleInfoMap[currentRole] = nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]

	   tempNodeInfoArr =	nodeConfig[Depac::CloudDeploymentConstants::NODE_INFO]
          Depac::Log.write("INFO", "hanlde the #{count}th node.")
	   tempArray = Array.new
          tempArray << tempNodeInfoArr[count-1]
	   roleInfoMap[currentRole]= tempArray
        end
      end
      nodeInfoParamMap[type] = roleInfoMap
    end
  end
  Depac::Log.write("INFO", "nodeInfoParamMap is: #{nodeInfoParamMap}")
  return nodeInfoParamMap
end
